/**
 * 
 */
package dk.itu.mobile.server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import dk.itu.mobile.util.CryptoBaws;

/**
 * @author group02
 *
 */
public class TokenServer {

	protected DatagramSocket socket = null;
	protected BufferedReader inBuffer = null;
	protected BufferedWriter outBuffer = null;
	private static final String secret = "tokenClientSecre";
	private static final String serverSecret = "super1337secret!";
	private static final int port = 4446;
	private static final Logger log = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
	private Map<String, Roles> roleMap;
	
	/**
	 * Starts the Token server which authenticates against ITU ssh using the ItuConnector class
	 * @param args - ignored
	 */
	public static void main(String[] args) {
		log.log(Level.INFO, "Token server started at port: "+port );
		TokenServer server = new TokenServer();
		server.start();
	}
	
	/**
	 * populates roles and calls listen
	 */
	public void start() {
		fillRoles();
		try {
			socket = new DatagramSocket(port);
			listen();
		} catch (SocketException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Start listening on the agreed port for Authentication requests</br>
	 * When a packet is received, authenticate is invoked.
	 */
	private void listen() {
		byte[] buffer = new byte[256];
		DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
		try {
			log.log(Level.INFO, "Token server listening for authentication requests");
			socket.receive(packet);
			log.log(Level.INFO,"Recieved request, now authenticating");
			
		} catch (IOException e) {
			log.log(Level.SEVERE, e.getMessage());
		}
		authenticate(packet);
	}
	
	/**
	 * Tries to authenticate the user against ITU ssh, using username & password. Uses the cryptobaws class
	 * for decryption, with the secret shared between client and TokenServer.</br>
	 * No matter the result of the authentication, respond is envoked</br>
	 * @param packet - the packet recieved from the client, containing username and password
	 */
	private void authenticate(DatagramPacket packet)  {
		String base64String = new String(packet.getData());
		String userPassString  = "";
		try {
			userPassString = CryptoBaws.decode(base64String, secret);
		} catch (Exception e) {
			log.log(Level.SEVERE, e.getMessage());
		}
		String[] userPass = userPassString.split(",");
		if (userPass.length > 1 && userPass[0] != null && userPass[1]!= null) {
			respond(packet, ItuConnector.authenticate(userPass[0], userPass[1]), userPass[0]);
		} else {
			log.log(Level.SEVERE, "username or password missing");
		}
			
		respond(packet, false, userPass[0]);
	}
	
	/**
	 * Returns the result of the authentication attempt, together with role and a time stamp in Unix format</br>
	 * If authentication was invalid, the role is still returned.</br>
	 * 
	 * @param packet the packet that originally started the authentication request
	 * @param result the result of authentication against ITU ssh
	 * @param userName the username for which authentication was attempted.
	 */
	private void respond(DatagramPacket packet, boolean result, String userName) {
		byte[] response = new byte[256];
		String responseString ="";
		if (result == true) {
			responseString = ((Roles) roleMap.get(userName.toLowerCase())).getRoleName();
			responseString += ",";
			responseString += new Date().getTime();
			log.log(Level.INFO, "responding with:"+responseString);
			try {
				responseString = CryptoBaws.encode(responseString, serverSecret);
			} catch (Exception e) {
				e.printStackTrace();
			}
			response = responseString.getBytes();
			DatagramPacket responsePacket = new DatagramPacket(response, response.length, packet.getAddress(), packet.getPort());
			try {
				socket.send(responsePacket);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		log.log(Level.INFO, "Authentication process complete, listening for next request");
		listen();
	}
	
	private void fillRoles() {
		roleMap = new HashMap<String, TokenServer.Roles>();
		roleMap.put("neri", Roles.STUDENT);
		roleMap.put("ptaa", Roles.TA);
		roleMap.put("jaje", Roles.TEACHER);
	}
	
	private enum Roles {
		STUDENT("student-01"), TEACHER("teacher"), TA("ta-01");
		
		private String roleName;
		
		private Roles (String roleName) {
			this.roleName = roleName;
		}
		public String getRoleName() {
			return roleName;
		}
		
	}
}
